package org.moonlight.jvm.instructions.references.array;

import org.moonlight.jvm.instructions.base.BytecodeReader;
import org.moonlight.jvm.instructions.base.Index16Instruction;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.OperandStack;
import org.moonlight.jvm.rtda.share.Object;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.symref.ClassRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;

/**
 * multi_a_new_array指令: 创建多维数组.
 *                       这个指令需要两个操作数:
 *                         1. 第一个操作数是个uint16索引，通过这个索引可以从运行时常量池中找到一个类符号引用，解析这个引用就可以得到 多维 数组类
 *                         2. 第二个操作数是个uint8整数，表示数组维度。
 *                       这两个操作数在字节码中紧跟在指令操作码后面，由FetchOperands()方法读取
 *                       multi_a_new_array指令还需要从操作数栈中弹出n个整数，分别代表每一个维度的数组长度。
 *                       在 a_new_array指令中，解析类符号引用后得到的是数组元素的类，而这里解析出来的直接就是数组类。
 *
 * @author Moonlight
 * @createTime 2023/9/15 15:43
 **/
public class MultiANewArray extends Index16Instruction {

    /** 数组维度 **/
    private int dimensions;

    @Override
    public void fetchOperands(BytecodeReader reader) {
        super.fetchOperands(reader);
        this.dimensions = reader.readInt8();
    }

    @Override
    public void execute(Frame frame) {
        // 获取运行时常量池
        RtConstantPool rtCp = frame.getMethod().getClazz().getRtCp();
        // 获取数组类的符号引用
        ClassRef classRef = (ClassRef) rtCp.getConstant(this.idx);
        // 通过符号引用解析出数组类
        Class arrClass = classRef.resolvedClass();

        OperandStack stack = frame.getOperandStack();
        // 每个维度的长度
        int[] counts = popAndCheckCounts(stack);
        // 构建一个多维数组对象
        Object arr = newMultiDimensionalArray(counts, arrClass);

        stack.pushRef(arr);
    }

    private int[] popAndCheckCounts(OperandStack stack) {
        int[] result = new int[this.dimensions];
        for (int i  = this.dimensions - 1; i >= 0; i--) {
            result[i] = stack.popInt();
            if (result[i] < 0) {
                throw new NegativeArraySizeException();
            }
        }
        return result;
    }

    private Object newMultiDimensionalArray(int[] counts, Class arrClass) {
        // 多维数组的长度
        int len = counts[0];
        Object array = arrClass.newArray(len);
        // 创建其他维度
        if (counts.length > 1) {
            Object[] refs = array.refs();
            for (int i = 0; i < refs.length; i++) {
                int[] copyCounts = new int[counts.length - 1];
                System.arraycopy(counts, 1, copyCounts, 0, counts.length - 1);
                refs[i] = newMultiDimensionalArray(copyCounts, arrClass.componentClass());
            }
        }
        return array;
    }

}
